//
//  WCProjectTreeController.m
//  WabbitCode
//
//  Created by William Towe on 1/29/09.
//  Copyright 2009 Revolution Software. All rights reserved.
//

#import "WCProjectTreeController.h"
#import "WCGroup.h"
#import "WCProjectOutlineView.h"
#import "NSTreeNode+WCExtensions.h"
#import "NSTreeController+WCExtensions.h"
#import "WCAddToProjectSheetController.h"
#import "WCProject.h"
#import "NSArray+WCExtensions.h"
#import "WCMacros.h"
#import "WCProjectGroup.h"
#import "WCBuildConfiguration.h"
#import "WCBuildStep.h"
#import "WCTextFile.h"
#import "AIArrayAdditions.h"
#import "WCAppController.h"
#import "WCBuildTarget.h"
#import "NSIndexPath+WCExtensions.h"
#import "WCBuildTargetsGroup.h"


@implementation WCProjectTreeController
#pragma mark NSOutlineViewDataSource Protocol
- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id < NSDraggingInfo >)info proposedItem:(id)item proposedChildIndex:(NSInteger)index {
	if (index == NSOutlineViewDropOnItemIndex)
		return NSDragOperationNone;
	
	NSArray *types = [[info draggingPasteboard] types];
	
	// we have our private pboard type
	if ([types containsObject:kWCProjectOutlineViewPboardType]) {
		NSArray *nodes = [self treeNodesAtIndexPaths:[NSKeyedUnarchiver unarchiveObjectWithData:[[info draggingPasteboard] dataForType:kWCProjectOutlineViewPboardType]]];
		NSUInteger indexMinusOne = index - 1;
		
		// can only drag root nodes
		if (!item) {
			for (NSTreeNode *node in nodes) {
				if (![[node representedObject] isRootLevelGroup])
					return NSDragOperationNone;
				else if ([[node indexPath] lastIndex] == index ||
						 [[node indexPath] lastIndex] == indexMinusOne)
					return NSDragOperationNone;
			}
			return NSDragOperationMove;
		}
		
		NSTreeNode *firstNode = [nodes firstObject];
		NSTreeNode *firstRealNode = [firstNode representedObject];
		id realItem = [item representedObject];
		Class class = nil;
		
		if ([firstRealNode isKindOfClass:[WCFile class]]) {
			// catch a special case, we can drag a WCTextFile onto a build target to make a new build step from that text file
			if ([realItem isKindOfClass:[WCBuildTarget class]]) {
				for (NSTreeNode *node in nodes) {
					if (![[node representedObject] isKindOfClass:[WCTextFile class]])
						return NSDragOperationNone;
				} 
			}
			// otherwise we can only drag onto a WCGroup or subclass
			else if (![realItem isKindOfClass:[WCGroup class]])
				return NSDragOperationNone;
			
			class = [WCFile class];
		}
		else if ([firstRealNode isKindOfClass:[WCBuildTarget class]]) {
			if (![realItem isKindOfClass:[WCBuildTargetsGroup class]])
				return NSDragOperationNone;
			
			class = [WCBuildTarget class];
		}
		else if ([firstRealNode isKindOfClass:[WCBuildStep class]]) {
			if (![realItem isKindOfClass:[WCBuildTarget class]])
				return NSDragOperationNone;
			
			class = [WCBuildStep class];
		}
		else
			return NSDragOperationNone;
		
		if ([nodes count] == 1 &&
			[item isParentOfNode:firstNode] &&
			([[firstNode indexPath] lastIndex] == index ||
			 [[firstNode indexPath] lastIndex] == indexMinusOne))
			return NSDragOperationNone;
		
		for (NSTreeNode *node in nodes) {
			id realNode = [node representedObject];
			
			// if they aren't all the same class reject the drop
			if (![realNode isKindOfClass:class])
				return NSDragOperationNone;
			// certain things aren't dragable
			else if (![realNode isDragable])
				return NSDragOperationNone;
			// cannot drop an item onto itself
			else if ([item isEqualTo:node])
				return NSDragOperationNone;
			// cannot drop an item onto a descendant of itself
			else if ([item isDescendantOfNode:node])
				return NSDragOperationNone;
		}
		
		if ([realItem isKindOfClass:[WCBuildTarget class]] &&
			 [firstRealNode isKindOfClass:[WCTextFile class]])
			return NSDragOperationCopy;
		else if (([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) &&
				 ([firstRealNode isKindOfClass:[WCBuildStep class]]))
			return NSDragOperationCopy;
		return NSDragOperationMove;
	}
	else {		
		if (!item || ![[item representedObject] isKindOfClass:[WCGroup class]])
			return NSDragOperationNone;
		return NSDragOperationCopy;
	}
}

- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id < NSDraggingInfo >)info item:(id)item childIndex:(NSInteger)index {
	NSArray *types = [[info draggingPasteboard] types];
	WCProject *project = [[[outlineView window] windowController] document];
	
	// we have our private pboard type
	if ([types containsObject:kWCProjectOutlineViewPboardType]) {
		NSArray *nodes = [self treeNodesAtIndexPaths:[NSKeyedUnarchiver unarchiveObjectWithData:[[info draggingPasteboard] dataForType:kWCProjectOutlineViewPboardType]]];
		NSIndexPath *indexPath = (!item)?[NSIndexPath indexPathWithIndex:index]:[[item indexPath] indexPathByAddingIndex:index];
		id firstRealNode = [[nodes firstObject] representedObject];
		id realItem = [item representedObject];
		
		if (!item) {
			[self moveNodes:nodes toIndexPath:indexPath];
			[self updateChildNodeIndexesForTreeNode:nil inProject:project];
			return YES;
		}
		// we are creating new build steps from the dragged text files
		else if ([realItem isKindOfClass:[WCBuildTarget class]] &&
				 [firstRealNode isKindOfClass:[WCTextFile class]]) {
			
			NSMutableArray *stepsToSelect = [NSMutableArray array];
			WCBuildTarget *target = (WCBuildTarget *)realItem;
			
			for (WCTextFile *tf in [nodes valueForKey:@"representedObject"]) {
				WCBuildStep *step = [WCBuildStep buildStepWithStepType:WCBSStepTypeAssembleFile file:tf];
				[stepsToSelect addObject:step];
				[[target mutableChildNodes] insertObject:step atIndex:index++];
			}
			
			[self selectRepresentedObjects:stepsToSelect];
			return YES;
		}
		// create mutable copies of the items being dragged
		else if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask) {
			NSMutableArray *copiesToSelect = [NSMutableArray array];
			
			for (id realNode in [nodes valueForKey:@"representedObject"]) {
				id mutableCopy = [[realNode mutableCopy] autorelease];
				
				[copiesToSelect addObject:mutableCopy];
				[[realItem mutableChildNodes] insertObject:mutableCopy atIndex:index++];
			}
			
			[self selectRepresentedObjects:copiesToSelect];
			return YES;
		}
		
		// if we didn't hit a special case, move the nodes normally
		[self moveNodes:nodes toIndexPath:indexPath];
		// update the underlying model
		[self updateChildNodeIndexesForTreeNode:item inProject:project];
		// select the objects that were moved
		[self selectRepresentedObjects:[nodes valueForKey:@"representedObject"]];
	}
	else {
		NSArray *filePaths = [[info draggingPasteboard] propertyListForType:NSFilenamesPboardType];
		NSNumber *indexAsObject = [NSNumber numberWithUnsignedInteger:index];
		
		[WCAddToProjectSheetController presentAddToProjectSheetWithInfoDictionary:[NSDictionary dictionaryWithObjectsAndKeys:project, kWCAddToProjectProjectKey, filePaths, kWCAddToProjectFilePathsKey, item, kWCAddToProjectGroupItemKey, indexAsObject, kWCAddToProjectGroupInsertionIndexKey, nil]];
	}
	return YES;
}

- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard {
	NSMutableArray *filePaths = [NSMutableArray array];
	for (id item in [items valueForKey:@"representedObject"]) {
		if ([item isKindOfClass:[WCFile class]])
			[filePaths addObject:[item absoluteFilePath]];
	}
	
	NSMutableArray *types = [NSMutableArray arrayWithObject:kWCProjectOutlineViewPboardType];
	if ([filePaths count])
		[types addObject:NSFilenamesPboardType];
	[pboard declareTypes:types owner:nil];
	[pboard setData:[NSKeyedArchiver archivedDataWithRootObject:[items valueForKey:@"indexPath"]] forType:kWCProjectOutlineViewPboardType];
	if ([filePaths count])
		[pboard setPropertyList:filePaths forType:NSFilenamesPboardType];
	return YES;
}

@end
